﻿//
// Created by Grishul Eugeny
//
// See license in License.txt
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;

namespace NCommander
{
	internal unsafe static class Win32
	{
		[StructLayout( LayoutKind.Sequential )]
		public struct SHFILEINFO
		{
			public IntPtr hIcon;
			public IntPtr iIcon;
			public uint dwAttributes;

			[MarshalAs( UnmanagedType.ByValTStr, SizeConst = 260 )]
			public string szDisplayName;

			[MarshalAs( UnmanagedType.ByValTStr, SizeConst = 80 )]
			public string szTypeName;
		};

		public class QUERY_SERVICE_CONFIG
		{
			public int dwServiceType;
			public int dwStartType;
			public int dwErrorControl;
			public string lpBinaryPathName;
			public string lpLoadOrderGroup;
			public int dwTagId;
			public string lpDependencies;
			public string lpServiceStartName;
			public string lpDisplayName;
		}

		public const uint SHGFI_ICON = 0x100;
		public const uint SHGFI_LARGEICON = 0x0;
		public const uint SHGFI_SMALLICON = 0x1;
		public const uint SHGFI_LINKOVERLAY = 0x8000;
		public const uint SHGFI_ADDOVERLAYS = 0x20;

		[DllImport( "shell32.dll", SetLastError = true )]
		public static extern IntPtr SHGetFileInfo( string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags );

		[DllImport( "user32.dll" )]
		public static extern int DestroyIcon( IntPtr hIcon );

		[DllImport( "kernel32.dll" )]
		public static extern IntPtr GetModuleHandle( string lpModuleName );

		public static Icon ExtractAssociatedIcon( string lpIconPath )
		{
			short iconIndex;
			return ExtractAssociatedIcon( lpIconPath, out iconIndex );
		}

		public static Icon ExtractAssociatedIcon( string lpIconPath, out short lpiIcon )
		{
			return MarshalIcon( Native.ExtractAssociatedIcon( GetModuleHandle( null ), lpIconPath, out lpiIcon ) );
		}

		public static Icon ExtractIcon( string filename, uint icon )
		{
			return MarshalIcon( Native.ExtractIcon( GetModuleHandle( null ), filename, icon ) );
		}

		private static class Native
		{
			[DllImport( "shell32.dll" )]
			public static extern IntPtr ExtractAssociatedIcon( IntPtr hInst, string lpIconPath, out short lpiIcon );

			[DllImport( "shell32.dll" )]
			public static extern IntPtr ExtractIcon( IntPtr hInst, string lpszExeFileName, uint nIconIndex );

			[DllImport( "advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true )]
			public static extern bool QueryServiceConfig( IntPtr handle, IntPtr result, int bufferSize, out int bytesNeeded );

			[StructLayout( LayoutKind.Sequential )]
			public struct QUERY_SERVICE_CONFIG
			{
				public int dwServiceType;
				public int dwStartType;
				public int dwErrorControl;
				public char* lpBinaryPathName;
				public char* lpLoadOrderGroup;
				public int dwTagId;
				public char* lpDependencies;
				public char* lpServiceStartName;
				public char* lpDisplayName;
			}
		}

		private static Icon MarshalIcon( IntPtr nativeIcon )
		{
			if( nativeIcon == IntPtr.Zero )
				return null;

			Icon result;

			using( var temporaryIcon = Icon.FromHandle( nativeIcon ) )
				result = ( Icon ) temporaryIcon.Clone();

			DestroyIcon( nativeIcon );
			return result;
		}

		[DllImport( "Advapi32.dll", SetLastError = true )]
		public static extern bool DeleteService( IntPtr handle );

		public static QUERY_SERVICE_CONFIG QueryServiceConfig( IntPtr handle )
		{
			int bytesNeeded;
			Native.QueryServiceConfig( handle, IntPtr.Zero, 0, out bytesNeeded );

			byte* buffer = stackalloc byte[bytesNeeded];
			Native.QUERY_SERVICE_CONFIG* nativeResult = ( Native.QUERY_SERVICE_CONFIG* ) buffer;

			Native.QueryServiceConfig( handle, new IntPtr( buffer ), bytesNeeded, out bytesNeeded );

			var result = new QUERY_SERVICE_CONFIG()
			{
				dwErrorControl = nativeResult->dwErrorControl,
				dwServiceType = nativeResult->dwServiceType,
				dwStartType = nativeResult->dwStartType,
				dwTagId = nativeResult->dwTagId,
				lpBinaryPathName = new string( nativeResult->lpBinaryPathName ),
				lpDependencies = new string( nativeResult->lpDependencies ),
				lpDisplayName = new string( nativeResult->lpDisplayName ),
				lpLoadOrderGroup = new string( nativeResult->lpLoadOrderGroup ),
				lpServiceStartName = new string( nativeResult->lpServiceStartName )
			};

			return result;
		}

		[Flags]
		public enum ServiceControlManagerAccessRights
		{
			SC_MANAGER_ALL_ACCESS = 0xF003F,
			SC_MANAGER_CREATE_SERVICE = 0x0002,
			SC_MANAGER_CONNECT = 0x0001,
			SC_MANAGER_ENUMERATE_SERVICE = 0x0004,
			SC_MANAGER_LOCK = 0x0008,
			SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020,
			SC_MANAGER_QUERY_LOCK_STATUS = 0x0010
		}

		[Flags]
		public enum ServiceAccessRights
		{
			SERVICE_ALL_ACCESS = 0xF01FF,
			SERVICE_CHANGE_CONFIG = 0x0002,
			SERVICE_ENUMERATE_DEPENDENTS = 0x0008,
			SERVICE_INTERROGATE = 0x0080,
			SERVICE_PAUSE_CONTINUE = 0x0040,
			SERVICE_QUERY_CONFIG = 0x0001,
			SERVICE_QUERY_STATUS = 0x0004,
			SERVICE_START = 0x0010,
			SERVICE_STOP = 0x0020,
			SERVICE_USER_DEFINED_CONTROL = 0x0100
		}

		[Flags]
		public enum ServiceType
		{
			SERVICE_ADAPTER = 0x00000004,
			SERVICE_FILE_SYSTEM_DRIVER = 0x00000002,
			SERVICE_KERNEL_DRIVER = 0x00000001,
			SERVICE_RECOGNIZER_DRIVER = 0x00000008,
			SERVICE_WIN32_OWN_PROCESS = 0x00000010,
			SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
			SERVICE_INTERACTIVE_PROCESS = 0x00000100
		}

		[Flags]
		public enum ServiceStartType
		{
			SERVICE_AUTO_START = 0x00000002,
			SERVICE_BOOT_START = 0x00000000,
			SERVICE_DEMAND_START = 0x00000003,
			SERVICE_DISABLED = 0x00000004,
			SERVICE_SYSTEM_START = 0x00000001
		}

		public enum ServiceErrorControl
		{
			SERVICE_ERROR_CRITICAL = 0x00000003,
			SERVICE_ERROR_IGNORE = 0x00000000,
			SERVICE_ERROR_NORMAL = 0x00000001,
			SERVICE_ERROR_SEVERE = 0x00000002
		}

		[DllImport( "Advapi32.dll", SetLastError = true )]
		public static extern IntPtr CreateService( IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, ServiceType dwServiceType, ServiceStartType dwStartType, ServiceErrorControl dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, out int lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword );

		[DllImport( "Advapi32.dll", SetLastError = true )]
		public static extern IntPtr OpenSCManager( string lpMachineName, string lpDatabaseName, ServiceControlManagerAccessRights dwDesiredAccess );

		[DllImport( "Advapi32.dll", SetLastError = true )]
		public static extern bool CloseServiceHandle( IntPtr hSCObject );
	}
}
